﻿Imports System.Reflection, EnvDTE, Ver = BaseProject.Mod_Utilities.Versioning


''' <summary>
''' 
''' </summary>
''' <remarks></remarks>
''' <features></features>
''' <stepthrough></stepthrough>
Public Class Injector
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Property ReferencedAssemblies As List(Of Assembly)
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <value></value>
    ''' <remarks></remarks>
    Public Property LocalMembers As List(Of LocalMember)


    ''' <summary>
    ''' 
    ''' </summary>
    ''' <param name="refAssemblies"></param>
    ''' <param name="localMembers__2"></param>
    ''' <remarks></remarks>
    ''' <stepthrough></stepthrough>
    Friend Sub New(ByVal refAssemblies As List(Of Assembly), ByVal localMembers__2 As List(Of LocalMember))
        ReferencedAssemblies = refAssemblies
        LocalMembers = localMembers__2
    End Sub


    Private Shared Check As Boolean = False
    ''' <summary>
    ''' 
    ''' </summary>
    ''' <remarks></remarks>
    ''' <stepthrough></stepthrough>
    Public Sub Execute()
        'The injector code into a dynamically compiled assembly compile
        Dim compiler__1 As New Compiler(Mod_Utilities.SourceCode, ReferencedAssemblies, LocalMembers, LocalsMode.PrivateClassFields)
        compiler__1.Compile()

        If compiler__1.Results.Errors.HasErrors Then
            Mod_Utilities.ResultControl.Enabled = False
            Mod_Utilities.ResultControl.TellSingleMessage(compiler__1.ErrorText)
        Else
            Mod_Utilities.Compiler = compiler__1
            LoadInjectorIntoDebuggee()

            Inject(Mod_Utilities.Versioning.FormatEnd("___injectorType.GetField(""m___this"").SetValue(___injector, this)"))
            'Da' ref 'for some reason does not work, copy the methods-Locals.   
            For Each field In Mod_Utilities.Compiler.LocalFields
                Inject(String.Format(Ver.FormatEnd("___injectorType.GetField(""{0}"").SetValue(___injector, {0})"), field.CompilableName))
            Next

            'Dim finalExp As EnvDTE100.Expression2 = Inject("___method.Invoke(___injector, new object[] { });") '<-- Do I need this
            Dim Result2 = compiler__1.Results.CompiledAssembly.CreateInstance("MethodInjector")
            Dim TextFormatter =
                Function(x As EnvDTE100.Expression2) As Object
                    Return If(Not IsNumeric(x.Value) AndAlso TypeOf x.Value Is String, x.Value.ToString.Remove(0, 1).Remove(x.Value.ToString.Length - 2), x.Value)
                End Function


            Dim ABC = compiler__1.Results.CompiledAssembly
            Dim B = ABC.CreateInstance("MethodInjector")
            If Check Then
                For Each Item In Result2.GetType.GetMembers                 'Find A way to only use Properties AND Fields But Not Methods as Well
                    If Not Item.MemberType = MemberTypes.Property AndAlso Not Item.MemberType = MemberTypes.Field Then Continue For
                    Dim Temp As Type, LocalMatch = Mod_Utilities.Local(Item.Name)
                    Dim MugMaster As New Muggers.SimpleMunger(Item.Name)
                    If LocalMatch Is Nothing Then Continue For

                    If TypeOf Item Is System.Reflection.FieldInfo Then
                        Temp = DirectCast(Item, System.Reflection.FieldInfo).FieldType
                    Else
                        Temp = DirectCast(Item, System.Reflection.PropertyInfo).PropertyType
                    End If


                    If Temp.IsSubclassOf(GetType(Array)) Then
                        Dim Thing As New ArrayList
                        LocalMatch.Items.ForEach(Sub(x) Thing.Add(TextFormatter(x)))
                        MugMaster.PutValue(B, Thing.ToArray(Thing(0).GetType))
                    ElseIf Temp.GetInterfaces.Contains(GetType(IList)) Then
                        Dim Test = Activator.CreateInstance(Temp)
                        LocalMatch.Items.ForEach(Sub(x) If Not x.Name = "Raw View" Then Test.Add(TextFormatter(x)))
                        MugMaster.PutValue(B, Test)

                    ElseIf Temp = GetType(String) Then
                        MugMaster.PutValue(B, TextFormatter(LocalMatch.Data))
                    Else
                        If Temp.GetConstructors.Count = 0 Then Continue For
                        Dim CanBuild As Boolean = False
                        For Each Builder In Temp.GetConstructors
                            If Builder.IsPublic = True AndAlso Builder.GetParameters.Length = 0 Then
                                CanBuild = True
                                Exit For
                            End If
                        Next

                        If CanBuild = True Then
                            Dim NewItem = Activator.CreateInstance(Temp)

                            If TypeOf NewItem Is IList Then
                                For Each Thing In LocalMatch.Items
                                    If Thing.Name.ToString.StartsWith("[0x") Then
                                        DirectCast(NewItem, IList).Add(Thing.Value)
                                    Else
                                        Dim Mug As New Muggers.SimpleMunger(Thing.Name)
                                        Mug.PutValue(NewItem, TextFormatter(Thing))
                                    End If
                                Next
                            Else
                                For Each Thing In LocalMatch.Items
                                    Dim Mug As New Muggers.SimpleMunger(Thing.Name)
                                    Mug.PutValue(NewItem, TextFormatter(Thing))
                                    MugMaster.PutValue(B, NewItem)
                                Next
                            End If
                        Else
                            'You have to somehow ReConstruct the Item Here without creating a New Instance


                        End If
                    End If
                Next
            End If

            Check = True
            Mod_Utilities.ResultControl.Enabled = True

            Try
                Dim Value As Object = B.InjectionMethod
                Mod_Utilities.ResultControl.DisplayExpression(Value)
            Catch ex As Exception
                Mod_Utilities.ResultControl.Enabled = False
                Mod_Utilities.ResultControl.TellSingleMessage(ex.ToString)
            End Try
        End If
    End Sub



    ''' <summary>Loads all Variables into the Debugger</summary>
    ''' <stepthrough>Disabled</stepthrough>
    <DebuggerStepThrough()>
    Private Sub LoadInjectorIntoDebuggee()
        Select Case Ver.Language
            Case Mod_Utilities.Versioning.Languages.CSharp
                'C# Adds Locals by the Normal Means
                Inject("System.Reflection.Assembly ___assembly;")
                Inject("Type ___injectorType;")
                Inject("object ___injector;")
                Inject("System.Reflection.MethodInfo ___method;")
                Inject("___assembly = System.Reflection.Assembly.LoadFile(@""" & Mod_Utilities.Compiler.OutputAssemblyPath & """);")
                Inject("___injectorType = ___assembly.GetType(""MethodInjector"");")
                Inject("___injector = ___injectorType.GetConstructor(new Type[] { }).Invoke(new object[] { });")
                Inject("___method = ___injectorType.GetMethod(""InjectionMethod"");")
            Case Ver.Languages.VB
                'VB.Net can only Add Locals by Assigning then A Value
                Inject("___assembly = System.Reflection.Assembly.LoadFile(""" & Mod_Utilities.Compiler.OutputAssemblyPath & """)")
                Inject("___injectorType = New Type")
                Inject("___injector = New Object")
                Inject("___method = New System.Reflection.MethodInfo")
                Inject("___injectorType = ___assembly.GetType(""MethodInjector"")")
                Inject("___injector = ___injectorType.GetConstructor(New Type() {}).Invoke(New Object() {})")
                Inject("___method = ___injectorType.GetMethod(""InjectionMethod"")")
        End Select
    End Sub


    ''' <summary>
    ''' Adds Code to the Debugger. Acts like Adding it to the Immediate Window
    ''' </summary>
    ''' <param name="statement">The Code you want to use</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    ''' <stepthrough>Enabled</stepthrough>
    <DebuggerStepThrough()>
    Private Function Inject(ByVal statement As String) As EnvDTE100.Expression2
        Return Mod_Utilities.DTE.Debugger.GetExpression(statement, True, 200)
    End Function



    ''' <summary>Do I even need this</summary> 
    <Obsolete("Does This Work....Did it Ever Work o.O", True)>
    Private Sub CopyModifiedLocalsFromInjector()
        'Da' ref 'for some reason does not work, get the values.      
        For Each field In Mod_Utilities.Compiler.LocalFields
            If Mod_Utilities.Versioning.Language = Mod_Utilities.Versioning.Languages.VB Then
                Inject(String.Format("{0} = DirectCast(___injectorType.GetField(""{0}"").GetValue(___injector), {1})", field.CompilableName, field.CompilableTypeName))
            Else
                Inject(String.Format("{0} = ({1})___injectorType.GetField(""{0}"").GetValue(___injector)", field.CompilableName, field.CompilableTypeName))
            End If
        Next
    End Sub


    ' ''' <summary>Inject the <see cref=" Mod_Utilities.Compiler">Compiler.LocalFields</see></summary> 
    '<DebuggerStepThrough()>
    'Private Sub CopyOriginalLocalsToInjector()
    '    Inject(Mod_Utilities.Versioning.FormatEnd("___injectorType.GetField(""m___this"").SetValue(___injector, this)"))

    '    'Da' ref 'for some reason does not work, copy the methods-Locals.   
    '    For Each field In Mod_Utilities.Compiler.LocalFields
    '        Inject(String.Format(Ver.FormatEnd("___injectorType.GetField(""{0}"").SetValue(___injector, {0})"), field.CompilableName))
    '    Next
    'End Sub

End Class